package ru.ntcreb.csir.libs.methods.SVCH.xml;

import java.io.StringWriter;
import java.io.Writer;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import ru.ntcreb.csir.libs.methods.SVCH.dto.SVCHImpulseDTO;
import ru.ntcreb.csir.libs.methods.SVCH.dto.TractDTO;

public class XMLBuilderService {

	private static final Logger log = LogManager
			.getLogger(XMLBuilderService.class);

	private final int MAX_ITERATIONS_DEFAULT = 10_000;
	private final int LOWER_TIME_BOUND_DEFAULT = 0;
	private final int UPPER_TIME_BOUND_DEFAULT = 10_000;

	public String createTractInputXML(TractDTO[] tract) {

		try {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

			DocumentBuilder db = dbf.newDocumentBuilder();

			Document doc = db.newDocument();

			Element rootElement = doc.createElement("data");
			doc.appendChild(rootElement);
			for (int i = 0; i < tract.length; i++) {
				createInnerTractXMLElement(doc, rootElement, tract[i]);
			}

			Transformer tr = TransformerFactory.newInstance().newTransformer();
			tr.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
			Writer writer = new StringWriter();

			tr.transform(new DOMSource(doc), new StreamResult(writer));

			return writer.toString();
		} catch (TransformerException | ParserConfigurationException e) {
			log.error(e.getMessage());
		}

		return "";

	}

	public String createSVCHImpulseInputXML(SVCHImpulseDTO svchImpulseDTO,
			Integer maxIterations, Integer lowerTimeBound,
			Integer upperTimeBound) {
		boolean bounds_priority = false;

		// priorities of input:
		// bounds
		// maxIterations
		// or default iterations

		if (lowerTimeBound != null && upperTimeBound != null
				&& (lowerTimeBound > 0 && upperTimeBound > 0)) {

			if (lowerTimeBound > upperTimeBound) {
				int temp = upperTimeBound;
				upperTimeBound = lowerTimeBound;
				lowerTimeBound = temp;
			}

			bounds_priority = true;
		} else if (maxIterations != null) {
			// set maxIterations by user input
		} else {
			maxIterations = MAX_ITERATIONS_DEFAULT;
		}

		try {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

			DocumentBuilder db = dbf.newDocumentBuilder();

			Document doc = db.newDocument();

			Element rootElement = doc.createElement("data");
			doc.appendChild(rootElement);
			
			Element entryElem = doc.createElement("entry");
			rootElement.appendChild(entryElem);

			Element impulseIncreaseConstant_Alpha = doc
					.createElement("impulseIncreaseConstant_Alpha");
			impulseIncreaseConstant_Alpha
					.setTextContent(String.valueOf(svchImpulseDTO
							.getImpulseIncreaseConstant_Alpha()));
			entryElem.appendChild(impulseIncreaseConstant_Alpha);

			Element impulseFallingConstant_Beta = doc
					.createElement("impulseFallingConstant_Beta");
			impulseFallingConstant_Beta.setTextContent(String
					.valueOf(svchImpulseDTO.getImpulseFallingConstant_Beta()));
			entryElem.appendChild(impulseFallingConstant_Beta);

			Element voltageAplitude_A = doc.createElement("voltageAplitude_A");
			voltageAplitude_A.setTextContent(String.valueOf(svchImpulseDTO
					.getVoltageAplitude_A()));
			entryElem.appendChild(voltageAplitude_A);

			Element impulseFillingFrequency_U0 = doc
					.createElement("impulseFillingFrequency_U0");
			impulseFillingFrequency_U0.setTextContent(String
					.valueOf(svchImpulseDTO.getImpulseFillingFrequency_U0()));
			entryElem.appendChild(impulseFillingFrequency_U0);

			if (bounds_priority) {
				Element lower_bound = doc.createElement("lowerTimeBound");
				lower_bound.setTextContent(String.valueOf(lowerTimeBound));
				rootElement.appendChild(lower_bound);

				Element upper_bound = doc.createElement("upperTimeBound");
				upper_bound.setTextContent(String.valueOf(upperTimeBound));
				rootElement.appendChild(upper_bound);
			} else {
				Element maxIterationsElem = doc.createElement("maxIterations");
				maxIterationsElem.setTextContent(String.valueOf(maxIterations));
				rootElement.appendChild(maxIterationsElem);
			}
			Transformer tr = TransformerFactory.newInstance().newTransformer();
			tr.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
			Writer writer = new StringWriter();

			tr.transform(new DOMSource(doc), new StreamResult(writer));

			return writer.toString();
		} catch (TransformerException | ParserConfigurationException e) {
			log.error(e.getMessage());
		}

		return "";

	}

	public String createTractSVCHReactionInputXML(
			Map<Double, Double> svchImpulseVoltageMap, TractDTO tract1,
			TractDTO tract2) {

		try {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

			DocumentBuilder db = dbf.newDocumentBuilder();

			Document doc = db.newDocument();

			Element rootElement = doc.createElement("data");
			doc.appendChild(rootElement);

			createInnerTractXMLElement(doc, rootElement, tract1);
			createInnerTractXMLElement(doc, rootElement, tract2);

			/*Element arrLenght = doc.createElement("arrLenght");
			arrLenght.setTextContent(String.valueOf(svchImpulseVoltageMap
					.size()));
			rootElement.appendChild(arrLenght);*/

			Element svchImpulseVoltageArray = doc
					.createElement("svchImpulseVoltageArray");
			rootElement.appendChild(svchImpulseVoltageArray);

			Iterator<Entry<Double, Double>> iter = svchImpulseVoltageMap
					.entrySet().iterator();
			while (iter.hasNext()) {
				Entry<Double, Double> entry = iter.next();

				Element entryElem = doc.createElement("entry");

				Element time = doc.createElement("inputTime");
				time.setTextContent(String.valueOf(entry.getKey()));
				Element voltage = doc.createElement("outputVoltage");
				voltage.setTextContent(String.valueOf(entry.getValue()));
				entryElem.appendChild(time);
				entryElem.appendChild(voltage);
				svchImpulseVoltageArray.appendChild(entryElem);
			}

			Transformer tr = TransformerFactory.newInstance().newTransformer();
			tr.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
			Writer writer = new StringWriter();

			tr.transform(new DOMSource(doc), new StreamResult(writer));

			return writer.toString();
		} catch (TransformerException | ParserConfigurationException e) {
			log.error(e.getMessage());
		}

		return "";

	}

	void createInnerTractXMLElement(Document doc, Element envelope,
			TractDTO tract) {

		if (tract == null)
			return;
		Element tractElem = doc.createElement("tract");

		Element approximationCoefficient_K = doc
				.createElement("approximationCoefficient_K");
		approximationCoefficient_K.setTextContent(String.valueOf(tract
				.getApproximationCoefficient_K()));
		tractElem.appendChild(approximationCoefficient_K);

		Element approximationCoefficient_N = doc
				.createElement("approximationCoefficient_N");
		approximationCoefficient_N.setTextContent(String.valueOf(tract
				.getApproximationCoefficient_N()));
		tractElem.appendChild(approximationCoefficient_N);

		Element voltage_U0 = doc.createElement("voltage_U0");
		voltage_U0.setTextContent(String.valueOf(tract.getVoltage_U0()));
		tractElem.appendChild(voltage_U0);

		Element stepness_S = doc.createElement("stepness_S");
		stepness_S.setTextContent(String.valueOf(tract.getStepness_S()));
		tractElem.appendChild(stepness_S);

		envelope.appendChild(tractElem);
	}

}